<script setup lang="ts">
import { reactive, ref, computed, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElButton, ElCard } from 'element-plus'
import { Menu } from '@element-plus/icons-vue'
import * as Tools from '@/tools'
import { toolsInfo } from '@/tools'
import { getOpenAICompletion } from '@/openai'

const route = useRoute()

// 弹窗当前位置（仅在未吸边状态下使用 left 定位）
const position = reactive({ x: 100, y: 100 })

// 是否吸附左右侧
const anchoredRight = ref(true)
const anchoredLeft = ref(false)

// 圆形直径
const SIZE = 48
const MARGIN = 10

// 控制展开/收起
const expanded = ref(false)

// 拖动相关状态
let dragging = false
let offsetX = 0
let offsetY = 0

// 聊天相关
interface ChatMsg {
  role: 'user' | 'assistant' | 'tool'
  content: string
  name?: string
}
const messages = reactive<ChatMsg[]>([])
const input = ref('')
const loading = ref(false)

function formatResult(res: any): string {
  if (res === undefined || res === null) return '操作已完成';
  if (typeof res === 'string') return res;
  if (typeof res === 'number' || typeof res === 'boolean') return String(res);
  if (Array.isArray(res)) return `共获取 ${res.length} 条数据。`;
  if (typeof res === 'object') return '操作成功。';
  return '操作已完成';
}

async function handleUserRequest() {
  loading.value = true
  let isDone = false
  let iterationCount = 0
  const MAX_ITERATIONS = 10

  while (!isDone && iterationCount < MAX_ITERATIONS) {
    iterationCount++
    try {
      const res = await getOpenAICompletion(messages, toolsInfo, route.path)
      
      const assistantContent: string = res.content ?? ''
      messages.push({ role: 'assistant', content: assistantContent })

      // 1) 去除 markdown 代码块包裹
      const cleanedContent = assistantContent.replace(/```[a-zA-Z]*[\s\n]*([\s\S]*?)```/g, '$1').trim()

      let parsed: any = null
      try {
        parsed = JSON.parse(cleanedContent)
      } catch (_) {
        // 回复中可能带有自然语言+JSON，尝试提取第一个 JSON 对象
        const jsonMatch = cleanedContent.match(/\{[\s\S]*\}/)
        if (jsonMatch) {
          try { parsed = JSON.parse(jsonMatch[0]) } catch (_) { /* ignore */ }
        }
      }

      if (!parsed) {
        isDone = true // 依旧不是有效 JSON，当作最终答复
        continue
      }

      if (parsed && parsed.done) {
        // Assistant indicates the workflow is completed
        messages.push({ role: 'assistant', content: '任务已完成' })
        isDone = true
      } else if (parsed && parsed.tool) {
        const tool = parsed.tool as string
        // 兼容助手直接把参数平铺在 JSON 顶层的写法
        const toolArgs = ('args' in parsed)
          ? (parsed as any).args
          : (() => {
              const { tool: _t, done: _d, ...rest } = parsed as any
              return Object.keys(rest).length ? rest : undefined
            })()
        const callId = (parsed as any).callId || undefined

        // 工具调用封装，处理参数形式差异
        const invokeTool = async (toolName: string, realArgs: any) => {
          const fn = (Tools as any)[toolName]
          if (!fn) throw new Error(`Tool '${toolName}' not found.`)

          // 数组直接展开
          if (Array.isArray(realArgs)) return fn(...realArgs)

          // 对象参数：如果函数只需要一个参数且对象中存在 id 字段，则取 id，
          // 主要解决 routerToTaskExecuteView({id:123}) 导致路径为 [object Object]
          if (realArgs && typeof realArgs === 'object') {
            if (fn.length === 1) {
              if ('id' in realArgs) {
                return fn((realArgs as any).id)
              }
              const keys = Object.keys(realArgs)
              if (keys.length === 1) {
                return fn((realArgs as any)[keys[0]])
              }
            }
            return fn(realArgs)
          }

          // 其余情况当作单一基本类型参数或无参
          return realArgs !== undefined ? fn(realArgs) : fn()
        }

        if (tool in Tools) {
          try {
            const result = await Promise.resolve(invokeTool(tool, toolArgs))
            // 1) 将执行结果作为 tool 消息回传给模型（但 UI 隐藏）
            const toolMsg: any = { role: 'tool', name: tool, content: JSON.stringify(result ?? '') }
            if (callId) toolMsg.tool_call_id = callId
            messages.push(toolMsg)

            // 2) 同时给用户友好提示
            messages.push({ role: 'assistant', content: formatResult(result) })

            // 如果是页面导航类工具，默认视为任务完成
            if (tool.startsWith('router')) {
              messages.push({ role: 'assistant', content: '页面已打开，任务完成。' })
              isDone = true
            } else {
              isDone = false // allow assistant to decide whether more steps are needed for other tools
            }
          } catch (e: any) {
            messages.push({ role: 'tool', content: `Error executing tool '${tool}': ${e.message}` })
            isDone = true // Stop on error
          }
        } else {
          messages.push({ role: 'tool', content: `Tool '${tool}' not found.` })
          isDone = true // Stop on error
        }
      } else {
        isDone = true // JSON but not a tool call
      }
    } catch (e) {
      messages.push({ role: 'assistant', content: '请求失败' })
      isDone = true // Stop on error
    }
  }
  loading.value = false

  if (iterationCount >= MAX_ITERATIONS) {
    messages.push({ role: 'assistant', content: '已达到最大尝试次数，流程自动结束。' })
  }
}

async function sendMsg() {
  if (!input.value.trim() || loading.value) return
  const userContent = input.value.trim()
  messages.push({ role: 'user', content: userContent })
  input.value = ''
  handleUserRequest()
}

function onMouseDown(e: MouseEvent) {
  // 只响应左键
  if (e.button !== 0) return
  dragging = true
  // 拖动开始时若已吸附，则先解除吸附
  if (anchoredRight.value || anchoredLeft.value) {
    anchoredRight.value = false
    anchoredLeft.value = false
    // 把按钮中心定位到当前光标位置
    position.x = e.clientX - SIZE / 2
  }
  offsetX = e.clientX - position.x
  offsetY = e.clientY - position.y
  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)
}

function onMouseMove(e: MouseEvent) {
  if (!dragging) return
  position.x = e.clientX - offsetX
  position.y = e.clientY - offsetY
}

function onMouseUp() {
  dragging = false
  document.removeEventListener('mousemove', onMouseMove)
  document.removeEventListener('mouseup', onMouseUp)

  // 鼠标释放时，判断是否靠近左右侧，决定吸附
  anchoredRight.value = false
  anchoredLeft.value = false
  const SNAP = 60 // 吸附判定距离
  if (position.x + SIZE / 2 > window.innerWidth - SNAP) {
    anchoredRight.value = true
  } else if (position.x < SNAP - SIZE / 2) {
    anchoredLeft.value = true
  }
}

function toggleExpand() {
  // 拖动完毕立即点击会触发 toggle，简单防抖可根据需求改进
  expanded.value = !expanded.value
}

const openLeft = computed(() => {
  // 向左展开条件：1) 已右吸附 2) 非左吸附且位于屏幕右半区
  return (
    anchoredRight.value ||
    (!anchoredLeft.value && position.x + SIZE / 2 > window.innerWidth / 2)
  )
})

const styleComputed = computed(() => {
  const top = position.y + 'px'
  if (anchoredRight.value) {
    return { top, right: `-${SIZE / 2}px` }
  }
  if (anchoredLeft.value) {
    return { top, left: `-${SIZE / 2}px` }
  }
  return { top, left: position.x + 'px' }
})

</script>

<template>
  <div
    class="global-popup"
    :class="{ expanded, anchoredRight, anchoredLeft }"
    :style="styleComputed"
    @mousedown.stop="onMouseDown"
  >
    <!-- 圆形主按钮始终存在 -->
    <el-button class="circle-btn" type="primary" circle @click.stop="toggleExpand">
      <el-icon :size="20"><Menu /></el-icon>
    </el-button>

    <!-- 展开面板 -->
    <transition name="fade-slide">
      <div v-if="expanded" class="panel" :class="{ right: openLeft }" @click.stop @mousedown.stop>
        <el-card class="chat-card" shadow="always">
          <template #header>
            助理
          </template>
          <div class="chat-body">
            <div v-for="(m,i) in messages" :key="i" :class="['msg', m.role]">
              <span>{{ m.content }}</span>
            </div>
          </div>
          <el-input
            class="chat-input"
            v-model="input"
            placeholder="输入消息并按 Enter..."
            size="small"
            @keyup.enter.native="sendMsg"
            :disabled="loading"
          >
            <template #append>
              <el-button class="send-btn" size="small" @click="sendMsg" :loading="loading">发送</el-button>
            </template>
          </el-input>
        </el-card>
      </div>
    </transition>
  </div>
</template>

<style scoped>
.global-popup {
  position: fixed;
  z-index: 9999;
  cursor: move;
  user-select: none;
  transition: width 0.2s ease, height 0.2s ease;
}

.global-popup.expanded {
  cursor: default;
}

/* 圆形按钮 element-plus */
.circle-btn {
  width: 48px !important;
  height: 48px !important;
  padding: 0 !important;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 面板 */
.panel {
  position: absolute;
  top: 0;
  left: calc(100% + 8px);
  background-color: #fff;
  border: 1px solid #409eff;
  border-radius: 4px;
  min-width: 160px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  width: 340px;
}

.panel.right {
  left: auto;
  right: calc(100% + 8px);
}

.panel .el-card__header {
  background-color: #409eff;
  color: #fff;
  padding: 8px 12px;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}

.panel .content {
  padding: 12px;
  display: flex;
  gap: 8px;
}

/* 动画 */
.fade-slide-enter-active,
.fade-slide-leave-active {
  transition: all 0.2s ease;
}
.fade-slide-enter-from,
.fade-slide-leave-to {
  opacity: 0;
  transform: translateX(10px);
}

/* 聊天区 */
.chat-body {
  max-height: 260px;
  overflow-y: auto;
  padding: 4px 6px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  scroll-behavior: smooth;
}

.msg {
  font-size: 13px;
  line-height: 1.4;
  max-width: 80%;
  word-break: break-word;
  padding: 6px 10px;
  border-radius: 12px;
}

.msg.user {
  align-self: flex-end;
  color: #fff;
  background-color: #409eff;
  border-bottom-right-radius: 2px;
}

.msg.assistant {
  align-self: flex-start;
  color: #303133;
  background-color: #f2f3f5;
  border-bottom-left-radius: 2px;
}

/* 隐藏 tool 消息，改用辅助 assistant 提示 */
.msg.tool {
  display: none;
}

/* 输入框与发送按钮 */
.chat-input {
  --el-input-border-color: transparent;
  --el-input-border-radius: 20px;
  --el-input-bg-color: #f5f7fa;
  flex: 1;
}

.chat-input .el-input__inner {
  background-color: var(--el-input-bg-color);
  border-radius: var(--el-input-border-radius) 0 0 var(--el-input-border-radius);
  padding: 8px 12px;
}

.send-btn {
  background-color: #409eff;
  color: #fff;
  border: none;
  border-radius: 0 var(--el-input-border-radius) var(--el-input-border-radius) 0;
  padding: 0 16px;
}

.send-btn:hover:not(.is-loading) {
  background-color: #66b1ff;
}

.chat-input .el-input-group__append {
  padding: 0;
  border: none;
  background-color: transparent;
}
</style> 